package redisenum

import (
	"errors"
	"fmt"
	"time"
	"yunj/app/enum/adenum"
	"yunj/app/enum/articleenum"
	"yunj/app/enum/linkenum"
	appModel "yunj/app/model"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/enum/redisenum"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/app/model"
	yunjConfig "yunj/pkg/yunj/config"
	"yunj/pkg/yunj/core/db"
	"yunj/pkg/yunj/core/enum"
	"yunj/pkg/yunj/core/log"
	"yunj/pkg/yunj/util"
)

type Key struct {
	redisenum.Key
}

// 定义redis key常量
const (
	// 测试
	KEY_TEST string = "test:"

	// 获取指定天的每小时的访问量
	KEY_HOUR_REQUEST_PV_BY_DATE = "hour.request.pv.by.date:"

	// 获取指定天的每小时的访问人数
	KEY_HOUR_REQUEST_UV_BY_DATE = "hour.request.uv.by.date:"

	// 获取指定天的每小时的登录人数
	KEY_HOUR_LOGIN_UV_BY_DATE = "hour.login.uv.by.date:"

	// 今日各省份的访问数
	KEY_TODAY_PROV_REQUEST_PV = "today.prov.request.pv"

	// 今日各省份的访问人数
	KEY_TODAY_PROV_REQUEST_UV = "today.prov.request.uv"

	// 文章正常已发布的数量
	KEY_ARTICLE_STATE_NORMAL_STATUS_PUBLISH_COUNT = "article.state:normal.status:publish.count"

	// 文章的浏览数量
	KEY_ARTICLE_VIEW_COUNT = "article.view.count"

	// 前台菜单html结构
	KEY_INDEX_CATEGORY_MENU_HTML_LAYOUT = "index.category.menu.html.layout"

	// 前台的链接
	KEY_INDEX_LINK_ITEMS = "index.link.items"

	// 前台对应位置广告
	KEY_INDEX_AD_ITEMS_BY_LOCATION = "index.ad.items.by.location:"
)

func (rk *Key) AllConstAttrs() map[string]map[string]any {
	return map[string]map[string]any{
		KEY_TEST: {
			"desc": "测试",
			"keySuffix": func(rk redisenum.KeyInterface) (suffix string, err error) {
				if args, ok := rk.GetArgs().(map[string]any); ok {
					if id, exists := args["id"]; exists {
						suffix, err = util.ToString(id)
						return
					}
				}
				err = errors.New("args参数异常")
				return
			},
			"value": func(rk redisenum.KeyInterface) (any, error) {
				return time.Now().Format("2006-01-02 15:04:05"), nil
			},
		},
		KEY_HOUR_REQUEST_PV_BY_DATE: {
			"desc": "获取指定天的每小时的访问量",
			"keySuffix": func(rk redisenum.KeyInterface) (suffix string, err error) {
				if args, ok := rk.GetArgs().(*time.Time); ok {
					suffix = args.Format("2006-01-02")
					return
				}
				err = errors.New("获取指定天的每小时的访问量的args参数异常")
				return
			},
			"value": func(rk redisenum.KeyInterface) (res any, err error) {
				args, ok := rk.GetArgs().(*time.Time)
				if !ok {
					err = errors.New("获取指定天的每小时的访问量的args参数异常")
					return
				}
				logDb := db.Model(&appModel.ClientLog{}).SetTableNameSuffix(args.Format("_200601"))
				tableName := logDb.GetTableName()
				data := map[int]int{}
				if logDb.IsExistsTableName(tableName) {
					startDate := args.Format("2006-01-02") + " 00:00:00"
					endDate := args.Format("2006-01-02") + " 23:59:59"
					sql := fmt.Sprintf(`select
											h,
											count(h) as num
										from
											(
											select
												hour(created_at) as h
											from
												%s
											where
												created_at >= '%s'
												and created_at < '%s'
										) as tmp
										group by h`, tableName, startDate, endDate)
					type HourItem struct {
						H   int `json:"h"`
						Num int `json:"num"`
					}
					var hourItems []*HourItem
					if dbErr := global.MySQL.Raw(sql).Scan(&hourItems).Error; dbErr != nil {
						global.Logger.Error(util.FormatError(dbErr, "获取指定天的每小时的访问量的原始数据获取异常！"))
						err = errors.New("获取指定天的每小时的访问量的原始数据获取异常！")
						return
					}
					for _, hourItem := range hourItems {
						data[hourItem.H] = hourItem.Num
					}
				}
				hRes := make([]int, 24)
				for h := 0; h < 24; h++ {
					hRes[h] = int(data[h])
				}
				res = hRes
				return
			},
		},
		KEY_HOUR_REQUEST_UV_BY_DATE: {
			"desc": "获取指定天的每小时的访问人数",
			"keySuffix": func(rk redisenum.KeyInterface) (suffix string, err error) {
				if args, ok := rk.GetArgs().(*time.Time); ok {
					suffix = args.Format("2006-01-02")
					return
				}
				err = errors.New("获取指定天的每小时的访问人数的args参数异常")
				return
			},
			"value": func(rk redisenum.KeyInterface) (res any, err error) {
				args, ok := rk.GetArgs().(*time.Time)
				if !ok {
					err = errors.New("获取指定天的每小时的访问人数的args参数异常")
					return
				}
				logDb := db.Model(&appModel.ClientLog{}).SetTableNameSuffix(args.Format("_200601"))
				tableName := logDb.GetTableName()
				data := map[int]map[string]bool{}
				if logDb.IsExistsTableName(tableName) {
					startDate := args.Format("2006-01-02") + " 00:00:00"
					endDate := args.Format("2006-01-02") + " 23:59:59"
					sql := fmt.Sprintf(`select
											ip,hour(created_at) as h
										from
											%s
										where
											created_at >= '%s'
											and created_at < '%s'`, tableName, startDate, endDate)
					type HourItem struct {
						H  int    `json:"h"`
						Ip string `json:"ip"`
					}
					var hourItems []*HourItem
					if dbErr := global.MySQL.Raw(sql).Scan(&hourItems).Error; dbErr != nil {
						global.Logger.Error(util.FormatError(dbErr, "获取指定天的每小时的访问人数的原始数据获取异常！"))
						err = errors.New("获取指定天的每小时的访问人数的原始数据获取异常！")
						return
					}
					for _, hourItem := range hourItems {
						hIps, exists := data[hourItem.H]
						if !exists {
							hIps = map[string]bool{}
						}
						hIps[hourItem.Ip] = true
						data[hourItem.H] = hIps
					}
				}
				hRes := make([]int, 24)
				for h := 0; h < 24; h++ {
					if hIps, exists := data[h]; exists {
						hRes[h] = len(hIps)
					} else {
						hRes[h] = 0
					}
				}
				res = hRes
				return
			},
		},
		KEY_HOUR_LOGIN_UV_BY_DATE: {
			"desc": "获取指定天的每小时的登录人数",
			"keySuffix": func(rk redisenum.KeyInterface) (suffix string, err error) {
				if args, ok := rk.GetArgs().(*time.Time); ok {
					suffix = args.Format("2006-01-02")
					return
				}
				err = errors.New("获取指定天的每小时的登录人数的args参数异常")
				return
			},
			"value": func(rk redisenum.KeyInterface) (res any, err error) {
				args, ok := rk.GetArgs().(*time.Time)
				if !ok {
					err = errors.New("获取指定天的每小时的登录人数的args参数异常")
					return
				}
				logDb := db.Model(&model.AdminMemberLog{}).SetTableNameSuffix(args.Format("_200601"))
				tableName := logDb.GetTableName()
				data := map[int]map[string]bool{}
				if logDb.IsExistsTableName(tableName) {
					startDate := args.Format("2006-01-02") + " 00:00:00"
					endDate := args.Format("2006-01-02") + " 23:59:59"
					sql := fmt.Sprintf(`select
											concat(member_id,'_',ip) as member_ip,hour(created_at) as h
										from
											%s
										where
											created_at >= '%s'
											and created_at < '%s' 
											and url = '/admin/login' 
											and method = 'POST' 
											and member_id <> 0`, tableName, startDate, endDate)
					type HourItem struct {
						H        int    `json:"h"`
						MemberIp string `json:"member_ip"`
					}
					var hourItems []*HourItem
					if dbErr := global.MySQL.Raw(sql).Scan(&hourItems).Error; dbErr != nil {
						global.Logger.Error(util.FormatError(dbErr, "获取指定天的每小时的登录人数的原始数据获取异常！"))
						err = errors.New("获取指定天的每小时的登录人数的原始数据获取异常！")
						return
					}
					for _, hourItem := range hourItems {
						hMemberIps, exists := data[hourItem.H]
						if !exists {
							hMemberIps = map[string]bool{}
						}
						hMemberIps[hourItem.MemberIp] = true
						data[hourItem.H] = hMemberIps
					}
				}
				hRes := make([]int, 24)
				for h := 0; h < 24; h++ {
					if hIps, exists := data[h]; exists {
						hRes[h] = len(hIps)
					} else {
						hRes[h] = 0
					}
				}
				res = hRes
				return
			},
		},
		KEY_TODAY_PROV_REQUEST_PV: {
			"desc": "今日各省份的访问数",
			"value": func(rk redisenum.KeyInterface) (res any, err error) {
				currTime := time.Now().In(time.Local)
				startDate := currTime.Format("2006-01-02") + " 00:00:00"
				endDate := currTime.Format("2006-01-02") + " 23:59:59"
				logDb := db.Model(&appModel.ClientLog{}).SetTableNameSuffix(currTime.Format("_200601"))
				tableName := logDb.GetTableName()
				data := map[string]int{}
				if logDb.IsExistsTableName(tableName) {
					sql := fmt.Sprintf(`select
											ip_province,count(ip_province) as num
										from
											%s
										where
											ip_province <> '' 
											and created_at >= '%s'
											and created_at < '%s' 
										group by ip_province`, tableName, startDate, endDate)
					type DataItem struct {
						Num        int    `json:"num"`
						IpProvince string `json:"ip_province"`
					}
					var dataItems []*DataItem
					if dbErr := global.MySQL.Raw(sql).Scan(&dataItems).Error; dbErr != nil {
						global.Logger.Error(util.FormatError(dbErr, "获取今日各省份的访问数的原始数据获取异常！"))
						err = errors.New("获取今日各省份的访问数的原始数据获取异常！")
						return
					}
					for _, item := range dataItems {
						data[item.IpProvince] = item.Num
					}
				}
				res = data
				return
			},
		},
		KEY_TODAY_PROV_REQUEST_UV: {
			"desc": "今日各省份的访问人数",
			"value": func(rk redisenum.KeyInterface) (res any, err error) {
				currTime := time.Now().In(time.Local)
				startDate := currTime.Format("2006-01-02") + " 00:00:00"
				endDate := currTime.Format("2006-01-02") + " 23:59:59"
				logDb := db.Model(&appModel.ClientLog{}).SetTableNameSuffix(currTime.Format("_200601"))
				tableName := logDb.GetTableName()
				data := map[string]map[string]bool{}
				if logDb.IsExistsTableName(tableName) {
					sql := fmt.Sprintf(`select
											ip,ip_province
										from
											%s
										where
											ip_province <> '' 
											and created_at >= '%s'
											and created_at < '%s' 
										group by ip,ip_province`, tableName, startDate, endDate)
					type DataItem struct {
						Ip         string `json:"ip"`
						IpProvince string `json:"ip_province"`
					}
					var dataItems []*DataItem
					if dbErr := global.MySQL.Raw(sql).Scan(&dataItems).Error; dbErr != nil {
						global.Logger.Error(util.FormatError(dbErr, "获取今日各省份的访问人数的原始数据获取异常！"))
						err = errors.New("获取今日各省份的访问人数的原始数据获取异常！")
						return
					}
					for _, item := range dataItems {
						if _, exists := data[item.IpProvince]; !exists {
							data[item.IpProvince] = map[string]bool{}
						}
						data[item.IpProvince][item.Ip] = true
					}
				}
				resData := map[string]int{}
				for _, prov := range yunjConfig.Provinces {
					if dataIps, exists := data[prov]; exists {
						resData[prov] = len(dataIps)
					} else {
						resData[prov] = 0
					}
				}
				res = resData
				return
			},
		},
		KEY_ARTICLE_STATE_NORMAL_STATUS_PUBLISH_COUNT: map[string]interface{}{
			"desc": "文章正常已发布的数量",
			"value": func(rk redisenum.KeyInterface) (any, error) {
				var count int64
				err := global.MySQL.Model(&appModel.Article{}).Where("state = ? AND status = ?", stateenum.NORMAL, articleenum.STATUS_PUBLISH).Count(&count).Error
				return count, err
			},
		},
		KEY_ARTICLE_VIEW_COUNT: map[string]interface{}{
			"desc": "文章的浏览数量",
			"value": func(rk redisenum.KeyInterface) (any, error) {
				type SumRes struct {
					TotalViewCount int64
				}
				var sumRes SumRes
				err := global.MySQL.Model(&appModel.Article{}).Select("SUM(view_count) as total_view_count").Scan(&sumRes).Error
				return sumRes.TotalViewCount, err
			},
		},
		KEY_INDEX_CATEGORY_MENU_HTML_LAYOUT: map[string]interface{}{
			"desc": "前台菜单html结构",
		},
		KEY_INDEX_LINK_ITEMS: map[string]interface{}{
			"desc": "前台的链接",
			"value": func(rk redisenum.KeyInterface) (any, error) {
				var links []appModel.Link
				err := global.MySQL.
					Model(&appModel.Link{}).
					Where("status = ? and state = ?", linkenum.STATUS_PUBLISH, stateenum.NORMAL).
					Order("sort asc").
					Select("id,link,`desc`").
					Find(&links).
					Error
				if err != nil {
					global.Logger.Error("获取前台的链接数据异常！", err)
				}
				return links, nil
			},
		},
		KEY_INDEX_AD_ITEMS_BY_LOCATION: map[string]interface{}{
			"desc": "前台对应位置广告",
			"keySuffix": func(rk redisenum.KeyInterface) (suffix string, err error) {
				location := rk.GetArgs()
				if suffix, err := util.ToString(location); err == nil {
					return suffix, nil
				} else {
					return "", fmt.Errorf("args参数转换为string类型错误！%v", err)
				}
			},
			"value": func(rk redisenum.KeyInterface) (any, error) {
				var ads []appModel.Ad
				location := rk.GetArgs()
				err := global.MySQL.
					Model(&appModel.Ad{}).
					Where("location = ? and status = ? and state = ?", location, adenum.STATUS_PUBLISH, stateenum.NORMAL).
					Order("sort asc").
					Select("id,cover,link,`desc`").
					Find(&ads).
					Error
				if err != nil {
					log.Error("获取前台对应位置广告数据异常！", err)
				}
				return ads, nil
			},
		},
	}
}

func (rk *Key) ByValue(value string) redisenum.KeyInterface {
	return KeyByValue(value)
}

// 存储已经创建的实例
var keyInstanceCache = make(map[string]*Key)

// 根据value获取枚举
func KeyByValue(value string) *Key {
	if instance, exists := keyInstanceCache[value]; exists {
		return instance
	}
	e := &Key{Key: redisenum.Key{}}
	e.Key.KeyInstance = e
	enum.SetEnumInstance(&e.Key.Enum, e, value)
	keyInstanceCache[value] = e
	return e
}

var KeyEnum *Key = func() *Key {
	e := &Key{Key: redisenum.Key{}}
	e.Key.KeyInstance = e
	enum.SetEnumInstance(&e.Key.Enum, e)
	return e
}()
